#include<iostream>
#include<string>
#include<functional>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<cstring>
#include<pthread.h>
#include<sys/types.h>
#include<sys/wait.h>

#include"InetAddr.hpp"
#include"Log.hpp"

enum 
{
    SOCKET_ERRPR = 1,
    BIND_ERROR,
    LISTEN_ERROR,
    USAGE_ERROR
};

const static int defaultsockfd = -1;
const static int gbackolg = 16;

class TcpServer;

class ThreadData//线程数据
{
    public:
    ThreadData(int fd,InetAddr addr,TcpServer *s)
    :sockfd(fd),
    clientaddr(addr),
    sefl(s)
    {}
    public:
    int sockfd;
    InetAddr clientaddr;
    TcpServer *sefl;
};

using func_t = std::function<std::string (const std::string &)>;

class TcpServer
{
    public:
    TcpServer(int port,func_t func)
    :_port(port)
    ,_listensock(defaultsockfd)
    ,_isrunning(false)
    ,_func(func)
    {}

    void InitServer()//创建流式套接字
    {
        _listensock = ::socket(AF_INET,SOCK_STREAM,0);
        if(_listensock<0)
        {
            LOG(FATAL,"socket error");
            exit(SOCKET_ERRPR);
        }
        LOG(DEBUG,"socket create successmsocket is %d\n",_listensock);
        //绑定
        struct sockaddr_in local;
        memset(&local,0,sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(_port);
        local.sin_addr.s_addr = INADDR_ANY;

        int n = ::bind(_listensock,(struct sockaddr*)&local,sizeof(local));
        if(n<0)
        {
            LOG(FATAL,"bind error");
            exit(BIG_ENDIAN);
        }
        LOG(DEBUG,"bind seccess sockfd is %d\n",_listensock);
        //tcp面向连接，先建建立连接，服务器式被链接的
        //服务器启动 首先等待客户到来
        //这个函数的主要作用是将一个主动的socket转换为一个被动的socket，
        //意味着它不再主动发起连接，而是等待并接受外来的连接请求。
        n = ::listen(_listensock,gbackolg);
        if(n<0)
        {
            LOG(FATAL,"listen error");
            exit(LISTEN_ERROR);
        }
        LOG(DEBUG,"lisen success sockfd is: %d\n",_listensock);
    }

    void Service(int sockfd,InetAddr client)
    {
        LOG(DEBUG,"get a new link ,info %s:%d,fd:%d\n",client.Ip().c_str(),client.Port(),sockfd);
        std::string clientaddr = "["+client.Ip()+":"+std::to_string(client.Port())+"]#";
        while (true)
        {
           char inbuffer[1024];
           //tmp面向字节流，不能保证inbuffer是一个完整的命令，
           //对于tcp必须保证在应用层面收到的是一个完成的请求；
           ssize_t n = recv(sockfd,inbuffer,sizeof(inbuffer)-1,0);
           if(n>0)
           {
            inbuffer[n] = 0;
            std::cout<<clientaddr<<inbuffer<<std::endl;

            std::string result = _func(inbuffer);
            
            send(sockfd,result.c_str(),result.size(),0);
           }
           else if(n ==0)
           {
            //客户退出关闭连接
            LOG(INFO,"%s quit\n",clientaddr.c_str());
            break;
           }
           else
           {
            LOG(ERROR,"read error\n",clientaddr.c_str());
            break;
           }
        }
        ::close(sockfd);
    }
    static void *HandlerSock(void *args)
    {
        pthread_detach(pthread_self());
        ThreadData *td = static_cast<ThreadData*>(args);
        td->sefl->Service(td->sockfd,td->clientaddr);
        delete td;
        return nullptr;
    }
    void Loop()
    {
        _isrunning = true;
        while (_isrunning)
        {
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            int sockfd = ::accept(_listensock,(struct sockaddr*)&peer,&len);
            if(sockfd<0)
            {
                LOG(WARNING,"accept errpr\n");
                continue;
            }
            //多线程
            pthread_t t;
            ThreadData *td = new ThreadData(sockfd,InetAddr(peer),this);
            //线程分离
            pthread_create(&t,nullptr,HandlerSock,td);
        }
        _isrunning = false;
        
    }
    ~TcpServer()
    {
        if (_listensock > defaultsockfd)
        {
            ::close(_listensock);
        }
    }

    private:
    uint16_t _port;
    int _listensock;
    bool _isrunning;

    func_t _func;
};